from math import pi
from sklearn.decomposition import PCA
from sklearn import manifold
import matplotlib.pyplot as plt
import collections
import numpy as np
from numpy import cos, sin
import random
from view.endSrc.MixedGaussianDatasetGenerator import MixedGaussianDatasetGenerator
from view.endSrc.tIniceRunner import tIniceRunner
from view.endSrc.MixedGaussianDataset import MixedGaussianDataset
from view.endSrc.Dataset import Dataset
import pandas as pd
from view.endSrc.DBConfig import DBConfig
from view.endSrc.MySqlConn import MySqlConn

import os
import re


def pcaVisual(meanList, dataset, labels):

    '''
    This method conduct the dimensionality reduction process by principle component analysis,
    which transform the data to 2D data, and plot the 2D data and centers by clusters.

    Examples:
    --------
    dataset.pcaVisual()
    '''

    k = len(meanList)
    dataACen = np.concatenate((meanList, dataset), axis=0)
    pca = PCA(n_components=2)
    pca.fit(dataACen)
    dataACen2d = pca.fit_transform(dataACen)
    center2d = dataACen2d[:k ,:]
    data2d = dataACen2d[k: ,:]

    plt.figure()
    for i in range(data2d.shape[0]):
        plt.scatter(data2d[i, 0], data2d[i, 1], marker='o', color=plt.cm.Set3(int(labels[i])), linewidths=0.3,
                    edgecolors='black')

    for i in range(center2d.shape[0]):
        plt.scatter(center2d[i, 0], center2d[i, 1], s=50, marker='D', color=plt.cm.Set3(i), linewidths=1,
                    edgecolors='black')
        text = "c" + str(i + 1)
        plt.annotate(text, xy=(center2d[i, 0], center2d[i, 1]),
                     xytext=(center2d[i, 0] + 1.5, center2d[i, 1] + 1.5))

    plt.xlabel('dimension 1')
    plt.ylabel('dimension 2')
    plt.title('Principle Component Analysis to 2D data')
    plt.savefig(fname='pca.svg', format='svg')
    plt.show()


def tsneVisual(meanList, dataset, labels):
    k = len(meanList)
    dataACen = np.concatenate((meanList, dataset), axis=0)

    tsne = manifold.TSNE(n_components=2, init='pca', random_state=501)
    x_tsne = tsne.fit_transform(dataACen)

    x_min, x_max = x_tsne.min(0), x_tsne.max(0)
    x_norm = (x_tsne - x_min) / (x_max - x_min)

    center2d = x_norm[:k, :]
    data2d = x_norm[k:, :]

    plt.figure()
    for i in range(data2d.shape[0]):
        plt.scatter(data2d[i, 0], data2d[i, 1], marker='o', color=plt.cm.Set3(int(labels[i])), linewidths=0.3, edgecolors='black')

    for i in range(center2d.shape[0]):
        plt.scatter(center2d[i, 0], center2d[i, 1], s=50, marker='D', color=plt.cm.Set3(i), linewidths=1, edgecolors='black')

    plt.title('t-SNE to 2D data')
    plt.savefig(fname='tsne.svg', format='svg')
    plt.show()


def plotClusterFeature(data, labels):
    cluidx = np.unique(labels)
    col = data.shape[1]
    k = len(cluidx)

    for d in range(col):
        bins = np.linspace(
            min(data[:, d]),
            max(data[:, d]),
            25)
        if len(np.unique(bins)) == 1:
            bins = 'auto'
        for i in range(len(cluidx)):
            plt.subplot(np.ceil(k / 2), 2, i+1)
            labelIds = np.where(labels == i)[0]
            plt.hist(data[labelIds, d], bins=bins, density=True)
            plt.xlim(dmin[d], dmax[d])
            plt.title('cluster' + str(i+1))
        plt.suptitle('Dimension' + str(d + 1))
        plt.tight_layout()
        plt.savefig(fname='d'+str(d+1)+'.svg', format='svg')
        plt.show()


def cluInfo(meanList, labels):
    cluidx = np.unique(labels)
    k = len(cluidx)

    ran = np.linspace(0, 360, k+1)

    angles_circle = [i * pi / 180 for i in ran]
    x = cos(angles_circle)
    y = sin(angles_circle)

    cluSize = np.array([len(np.where(labels == i)[0]) for i in range(k)])
    cluSize = cluSize / max(cluSize) * 10000

    plt.figure()
    # plot the clusters and show the size
    for i in range(k):
        plt.scatter(x[i], y[i], s=cluSize[i], marker='o', color=plt.cm.Set3(i), linewidths=1, edgecolors='black')
        plt.xlim(-1.5, 1.5)
        plt.ylim(-1.5, 1.5)

    # calc distances between clusters and plot
    disMat = np.zeros((k, k))
    for i in range(k):
        for j in range(k):
            disMat[i, j] = np.sqrt(np.sum(np.square(meanList[i, :] - meanList[j, :])))
    disMat = disMat / disMat.max() * 5

    for i in range(k):
        for j in range(k):
            plt.plot([x[i], x[j]], [y[i], y[j]], linewidth=disMat[i, j], c='black')

    plt.savefig(fname='cluinfo.svg', format='svg')
    plt.show()


def visual2D(meanList, dataset, labels):

    k = len(meanList)
    for i in range(dataset.shape[0]):
        plt.scatter(dataset[i, 0], dataset[i, 1], marker='o', color=plt.cm.Set3(int(labels[i])), linewidths=0.3,
                edgecolors='black')
    for i in range(k):
        plt.scatter(meanList[i, 0], meanList[i, 1], s=50, marker='D', color=plt.cm.Set3(i), linewidths=1,
                    edgecolors='black')
        text = "c" + str( i +1)
        plt.annotate(text, xy = (meanList[i][0], meanList[i][1]),
                     xytext = (meanList[i][0], meanList[i][1]))


    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('2D origin data')
    plt.savefig(fname='2d.svg', format='svg')
    plt.show()


def saveToDB():

    ps = []
    for i in range(5):
        ps.append(1/5)

    print(ps)
    g = MixedGaussianDatasetGenerator(numOfInstances=1000, numOfFeatures=2,
                                      overlap=1,
                                      paiList=ps,
                                      xRange=[10, 1000])

    g.genSeparableComponents()
    dataset = g.genMixedDataset()

    # pcaVisual(dataset.getTrueCentres(), dataset, dataset.getLabels())
    visual2D(dataset.getTrueCentres(), dataset, dataset.getLabels())

    yes = input('Want to save to DB?: ')
    if yes == 'y':
        sqlConn = MySqlConn(DBConfig())
        dataset.saveToDB(sqlConn, 'mixGaus')


def loadFromDB(id):
    sqlConn = MySqlConn(DBConfig())
    dataset = Dataset.fromDB(sqlConn, id)
    return dataset


def getfilename(strn, path):
    names = []
    for dirpath, dirnames, filenames in os.walk(path):
        for fn in filenames:
            str1 = re.compile('^' + strn + '''(.*?).csv$''')
            match_obj = re.findall(str1, fn)
            if match_obj:
                names.append(fn)
    if len(names) == 1:
        return names[0]
    return None


def concatLabels(dataset, labels, runnerid, colnames):
    newdata = np.concatenate((labels[:, np.newaxis], dataset), axis=1)
    df = pd.DataFrame(newdata, columns=colnames)
    df.to_csv('../externel_data/'+dataset.t.name+'_runnerid'+str(runnerid)+'.csv', index=True, header=True)


if __name__ == '__main__':
    runnerid = 339

    colnames = ['label', 'number_of_nodes', 'graph_density', 'node_degree_avg', 'node_degree_max', 'eccentricity_avg', 'eccentricity_max']

    dmax = np.array([203., 1., 10.56, 132., 5.46556474, 53.])
    dmin = np.array([2.00000000e+00, 3.49446709e-03, 8.69565217e-02,
                     1.00000000e+00, 1.73010381e-03, 1.00000000e+00])

    # -----------------------------------------------------------------------------------------
    kmeantcsfilename = getfilename('kmeansCenters_runnerid'+str(runnerid), '../data/')
    labelfilename = getfilename('kmeansLabels_runnerid'+str(runnerid), '../data/')

    if kmeantcsfilename is None or labelfilename is None:
        exit('fail to find the file match runnerid')

    kmeantcsfilename = '../data/' + kmeantcsfilename
    labelfilename = '../data/' + labelfilename
    print(kmeantcsfilename)
    print(labelfilename)

    sqlConn = MySqlConn(DBConfig())
    t = tIniceRunner(sqlConn)
    t.readRow(runnerid)
    dsid = t.datasetId

    dataset = loadFromDB(dsid)
    labels = np.loadtxt(open(labelfilename, "rb"), delimiter=",", skiprows=0)
    tcs = np.loadtxt(open(kmeantcsfilename, "rb"), delimiter=",", skiprows=0)

    # idxs = np.random.randint(0, dataset.shape[0], 20000)
    # idxs.sort()
    # print(collections.Counter(labels))
    #
    # dataset = dataset[idxs, :]
    # labels = labels[idxs]

    print(collections.Counter(labels))

    # visual2D(tcs, dataset, labels)
    # pcaVisual(tcs, dataset, labels)
    # tsneVisual(tcs, dataset, labels)
    #
    plotClusterFeature(dataset, labels)
    # cluInfo(tcs, labels)
    # concatLabels(dataset, labels, runnerid, colnames)
